package com.alks.function.service.impl.finance.ryfinance;

import com.alks.common.aop.AutoPageAop;
import com.alks.common.data.dto.SysUserTokenDTO;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.service.RedisService;
import com.alks.common.utils.beanutils.ArrayUtils;
import com.alks.common.utils.beanutils.BeanUtil;
import com.alks.entity.data.entity.*;
import com.alks.entity.data.page.PageParam;
import com.alks.entity.data.response.ResponseInfo;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.data.dto.finance.ryfinance.ApAmtHeadDto;
import com.alks.function.data.request.finance.ryfinance.ApAmtHeadRequest;
import com.alks.function.data.vo.finance.mtlcosts.YfApExportVo;
import com.alks.function.mapper.PcOrderHeadMapping;
import com.alks.function.mapper.finance.ryfinance.PcMtlPreOtherMapper;
import com.alks.function.mapper.finance.ryfinance.YfApAmtFkMapper;
import com.alks.function.mapper.finance.ryfinance.YfApAmtMapper;
import com.alks.function.mapper.finance.ryfinance.YfCostDetailMapper;
import com.alks.function.service.finance.ryfinance.IApAmtService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.additional.query.impl.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.alks.common.utils.beanutils.ArrayUtils.isEmpyt;
import static com.alks.common.utils.beanutils.ArrayUtils.isNoEmpyt;
import static com.alks.entity.data.enums.RedisKeyEnum.RS_AP_AMT;
import static com.alks.entity.data.enums.ResultCodeEnum.*;

/**
 * @author MS
 * @since 2024/5/27下午5:03
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ApAmtServiceImpl extends ServiceImpl<YfApAmtMapper, YfApAmt> implements IApAmtService {
    private final YfApAmtMapper amtMapper;
    private final YfApAmtFkMapper fkMapper;
    private final YfCostDetailMapper costMapper;
    private final RedisService redisService;
    private final PcMtlPreOtherMapper otherMapper;
    private final PcOrderHeadMapping headMapper;

    private final String Y = "Y";

    @Override
    @AutoPageAop
    public Map<String, Object> apAmtHeadQuery(ApAmtHeadRequest request) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<ApAmtHeadDto> dtos = amtMapper.apAmtHeadQuery(request, companyId);
        /*获取金额之和*/
        BigDecimal amtSum = amtMapper.apAmtHeadSum(request, companyId);
        /*封装返回*/
        Map<String, Object> map = new HashMap<>(3);
        map.put("amtSum", amtSum);
        map.put("dtos", dtos);
        if (request.getPageNum()!=-1&& request.getPageSize()!=-1) {
            Page<ApAmtHeadDto> page = (Page<ApAmtHeadDto>) dtos;
            map.put("total", page.getTotal());
        }
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseInfo apAmtHeadAdd(String financeYymm, String vendorName) {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String userName = tokenDTO.getUserName();
        String companyId = tokenDTO.getCompanyId();
        /*入参检测*/
        RecTbAccountServiceImpl.financeDateCheck(financeYymm);
        /*费用结算数据*/
        List<YfApAmt> amtListAdd = costMapper.apAmpAddQuery(financeYymm,vendorName,companyId);
        List<YfApAmt> amtList = otherMapper.apAmpAddQuery(financeYymm,vendorName,companyId);
        /*获取收货对账数据*/
        if (ArrayUtils.isNoEmpyt(amtListAdd)){
            amtList.addAll(amtListAdd);
        }
        if (ArrayUtils.isEmpyt(amtList)) {
            log.warn("\n异常接口：apAmtHeadAdd\n异常原因：无可结算数据\n参数：月份：{}，厂商：{}", financeYymm, vendorName);
            return ResponseInfo.error("无可结算数据，请检查厂商和月份是否填写正确", INPUT_ERROR.getCode());
        }
        /*写入数据*/
        String slipNo = redisService.slipNoCreat(RS_AP_AMT.getKey());
        BigDecimal item = BigDecimal.ZERO;
        for (YfApAmt amt : amtList) {
            item = item.add(BigDecimal.ONE);
            amt.setCfmDate(null);
            amt.setCfmFlag(null);
            amt.setCfmUser(null);
            amt.setSysUser(userName);
            amt.setSysDate(LocalDateTime.now());
            amt.setSlipDate(LocalDate.now());
            amt.setSlipNo(slipNo);
            amt.setItem(item);
            amtMapper.insert(amt);
        }
        /*更新费用结算表中的结算单号*/
        costMapper.apAmtAddUp(companyId,financeYymm,vendorName,slipNo);
        otherMapper.apAmtAddUp(companyId,financeYymm,vendorName,slipNo);
        return ResponseInfo.ok();
    }

    @Override
    public ResponseInfo apAmtHeadDel(String slipNo) {
        String companyId = UserIdThread.get().getCompanyId();
        String cfm = "Y";
        /*删除数据*/
        LambdaQueryWrapper<YfApAmt> amtWrapper = new LambdaQueryWrapper<YfApAmt>()
                .eq(YfApAmt::getSlipNo, slipNo)
                .eq(YfApAmt::getCompanyId, companyId);
        List<YfApAmt> yfApAmts = amtMapper.selectList(amtWrapper);
        if (ArrayUtils.isEmpyt(yfApAmts)){
            return ResponseInfo.error("单据不存在");
        }
        List<YfApAmt> cfmList = yfApAmts.stream()
                .filter(amt -> cfm.equals(amt.getCfmFlag()))
                .collect(Collectors.toList());
        if (ArrayUtils.isNoEmpyt(cfmList)){
            throw new ServiceErrorException("存在已检核数据,不能取消结算");
        }
        amtMapper.delete(amtWrapper);
        /*更新费用结算明细*/
        LambdaUpdateWrapper<YfCostDetail> costWrapper = new LambdaUpdateWrapper<YfCostDetail>()
                .eq(YfCostDetail::getBalanceNo, slipNo)
                .eq(YfCostDetail::getCompanyId, companyId)
                .set(YfCostDetail::getBalanceNo, null);
        costMapper.update(null, costWrapper);
        /*更新收货对账数据*/
        LambdaUpdateWrapper<PcMtlPreOther> otherWrapper = new LambdaUpdateWrapper<PcMtlPreOther>()
                .eq(PcMtlPreOther::getBalanceNo, slipNo)
                .eq(PcMtlPreOther::getCompanyId, companyId)
                .set(PcMtlPreOther::getBalanceNo, null);
        otherMapper.update(null, otherWrapper);
        return ResponseInfo.ok();
    }

    @Override
    @AutoPageAop
    public Map<String, Object> apAmtDetailQuery(PageParam request, String slipNo,boolean smr) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        LambdaQueryChainWrapper<YfApAmt> wrapper = lambdaQuery()
                .eq(YfApAmt::getSlipNo, slipNo)
                .eq(YfApAmt::getCompanyId, companyId);
        List<YfApAmt> dtos = wrapper.list();
        /*处理汇总*/
        if (smr){
            Function<YfApAmt, List<Object>> keyExtractor = e ->
                    Arrays.asList(e.getMtlName(), e.getMtlUnit(), e.getMtlColor(), e.getMtlSpec());
            Map<List<Object>, List<YfApAmt>> aggregatedData = dtos.stream().collect(
                    Collectors.groupingBy(keyExtractor));
            dtos = new ArrayList<>();
            for (Map.Entry<List<Object>, List<YfApAmt>> entry : aggregatedData.entrySet()) {
                YfApAmt apAmt = entry.getValue().get(0);
                apAmt.setQty(BigDecimal.valueOf(entry.getValue().stream().mapToLong(e->e.getQty().longValue()).sum()));
                apAmt.setAmt(BigDecimal.valueOf(entry.getValue().stream().mapToDouble(e->e.getAmt().doubleValue()).sum()));
                dtos.add(apAmt);
            }
        }
        /*获取求和*/
        List<YfApAmt> amts = wrapper.select(YfApAmt::getAmt, YfApAmt::getQty).list();
        BigDecimal amtSum = amts.stream()
                .map(YfApAmt::getAmt)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal qtySum = amts.stream()
                .map(YfApAmt::getQty)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        Map<String, Object> map = new HashMap<>(4);
        map.put("dtos", dtos);
        if (!request.getPageNum().equals(-1) &&! request.getPageSize().equals(-1)) {
            Page<YfApAmt> page = (Page<YfApAmt>) dtos;
            map.put("total", page.getTotal());
        }
        map.put("amtSum", amtSum);
        map.put("qtySum", qtySum);
        return map;
    }

    @Override
    public ResponseInfo apAmtCfm(List<String> slipNos, String cfmFlag) {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String userName = tokenDTO.getUserName();
        String companyId = tokenDTO.getCompanyId();
        String cfm = "Y";
        /*入参检测*/
        if (isEmpyt(slipNos)) {
            log.error("\n异常接口：apAmtCfm\n异常原因：未传入单据号码");
            return ResponseInfo.error("请勾选要处理的单据", IN_IS_EMPTY.getCode());
        }
        /*获取数据*/
        LambdaUpdateWrapper<YfApAmt> wrapper = new LambdaUpdateWrapper<YfApAmt>()
                .eq(YfApAmt::getCompanyId, companyId)
                .in(YfApAmt::getSlipNo, slipNos);
        List<YfApAmt> amts = amtMapper.selectList(wrapper);
        if (amts == null) {
            log.error("\n错误接口：apAmtCfm\n错误原因：没有找到数据\n传入参数：{}", slipNos);
            return ResponseInfo.error("数据异常", DATA_IS_ERR.getCode());
        }
        List<String> findSlipNos = amts.stream().map(YfApAmt::getSlipNo).collect(Collectors.toList());
        Set<String> flagSet = amts.stream().map(YfApAmt::getCfmFlag).collect(Collectors.toSet());
        /*判断数据是否正确可以修改*/
        List<String> missSlipNos = ArrayUtils.findMiss(findSlipNos, slipNos);
        if (!isEmpyt(missSlipNos)) {
            log.error("\n错误接口：apAmtCfm\n错误原因：查询到了超出查询范围的单据号（可能是SQL注入）\n传入参数：{}", slipNos);
            return ResponseInfo.error("数据异常", INPUT_ERROR.getCode());
        } else if (flagSet.contains(cfmFlag)) {
            String msg = "Y".equals(cfmFlag) ? "检核已检核数据" : "取消检核未检核数据";
            log.error("\n错误接口：apAmtCfm\n错误原因：尝试{}\n传入参数：{}", msg, slipNos);
            return ResponseInfo.error("请勿" + msg, INPUT_ERROR.getCode());
        }
        /*如果取消检核 前提是不能有付款记录*/
        if (cfmFlag == null || cfmFlag.isEmpty()) {
            LambdaQueryWrapper<YfApAmtFk> fkWrapper = new LambdaQueryWrapper<YfApAmtFk>()
                    .select(YfApAmtFk::getSlipNo)
                    .eq(YfApAmtFk::getCompanyId, companyId)
                    .in(YfApAmtFk::getSlipNo, slipNos)
                    .groupBy(YfApAmtFk::getSlipNo);
            Set<String> setSlipNos = fkMapper.selectList(fkWrapper).stream().map(YfApAmtFk::getSlipNo).collect(Collectors.toSet());
            if (isEmpyt(setSlipNos)) {
                log.error("\n错误接口：apAmtCfm\n错误原因：尝试取消检核已应付数据{}", slipNos);
                return ResponseInfo.error("请勿取消检核已应付数据," + setSlipNos, INPUT_ERROR.getCode());
            }
        }
        /*更新标志位*/
        if (cfm.equals(cfmFlag)) {
            wrapper.set(YfApAmt::getCfmFlag, cfmFlag)
                    .set(YfApAmt::getCfmDate, LocalDate.now())
                    .set(YfApAmt::getCfmUser, userName);
        } else {
            /*判断是否是本人数据*/
            if (!amts.stream().allMatch(e -> e.getCfmUser().equals(userName))) {
                return ResponseInfo.error("非本人数据无法取消");
            }
            wrapper.set(YfApAmt::getCfmFlag, null)
                    .set(YfApAmt::getCfmDate, null)
                    .set(YfApAmt::getCfmUser, null);
        }
        amtMapper.update(null, wrapper);
        return ResponseInfo.ok();
    }

    @Override
    public ResponseInfo apAmtFkQuery(String slipNo) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        LambdaQueryWrapper<YfApAmtFk> wrapper = new LambdaQueryWrapper<YfApAmtFk>()
                .eq(YfApAmtFk::getCompanyId, companyId)
                .eq(YfApAmtFk::getSlipNo, slipNo);
        List<YfApAmtFk> dtos = fkMapper.selectList(wrapper);
        BigDecimal amtSum = dtos.stream()
                .map(YfApAmtFk::getAmt)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        Map<String, Object> map = new HashMap<>(2);
        map.put("dtos", dtos);
        map.put("amtSum", amtSum);
        return ResponseInfo.ok(map);
    }

    @Override
    @Transactional
    public ResponseInfo apAmtFkUp(List<YfApAmtFk> request) {
        String userName = UserIdThread.get().getUserName();
        String companyId = UserIdThread.get().getCompanyId();
        /*判断单据号是否为空*/
        List<YfApAmtFk> emptyList = request.stream().filter(r -> StringUtils.isBlank(r.getSlipNo())).collect(Collectors.toList());
        if (isNoEmpyt(emptyList)){
            return ResponseInfo.error("单据号不能为空", IN_IS_EMPTY.getCode());
        }
        /*判断单据号是否唯一*/
        Set<String> slipNoSet = request.stream().map(YfApAmtFk::getSlipNo).collect(Collectors.toSet());
        if (slipNoSet.size() > 1) {
            log.error("\n错误接口：apAmtFkUp\n错误原因：存在多组单据号中的数据\n传入单据号：{}", slipNoSet);
            return ResponseInfo.error("不能同时修改多条单据的应付记录", INPUT_ERROR.getCode());
        }
        String slipNo = request.get(0).getSlipNo();
        /*判断是否存在未检核数据，未检核不能修改*/
        fkUpCheck(slipNo, companyId);
        LambdaQueryWrapper<YfApAmtFk> wrapper = new LambdaQueryWrapper<YfApAmtFk>()
                .eq(YfApAmtFk::getCompanyId, companyId)
                .eq(YfApAmtFk::getSlipNo, slipNo);
        fkMapper.delete(wrapper);
        /*修改数据*/
        BigDecimal item = BigDecimal.ZERO;
        for (YfApAmtFk r : request) {
            r.setCompanyId(companyId);
            r.setSysUser(userName);
            r.setSysDate(LocalDateTime.now());
            r.setItem(item);
            fkMapper.insert(r);
            item = item.add(BigDecimal.ONE);
        }
        return ResponseInfo.ok();
    }

    @Override
    public ResponseInfo apAmtFkDel(String slipNo, BigDecimal item) {
        String companyId = UserIdThread.get().getCompanyId();
        /*判断是否存在未检核数据，未检核不能修改*/
        fkUpCheck(slipNo, companyId);
        /*删除数据*/
        LambdaQueryWrapper<YfApAmtFk> wrapper = new LambdaQueryWrapper<YfApAmtFk>()
                .eq(YfApAmtFk::getCompanyId, companyId)
                .eq(YfApAmtFk::getSlipNo, slipNo)
                .eq(YfApAmtFk::getItem, item);
        fkMapper.delete(wrapper);
        return ResponseInfo.ok();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseInfo aftPayProcess(List<String> slipNos) {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String companyId = tokenDTO.getCompanyId();
        String userName = tokenDTO.getUserName();
        /*获取尾款处理数据*/
        List<YfApAmtFk> fks=amtMapper.aftPayProcessQuery(slipNos,companyId);
        fks=fks.stream()
                .filter(a->a.getAmt()==null||a.getAmt().compareTo(BigDecimal.ZERO)>0)
                .collect(Collectors.toList());
        /*写入数据*/
        for (YfApAmtFk fk : fks) {
            fk.setSysDate(LocalDateTime.now());
            fk.setSysUser(userName);
            fk.setApCheckDate(LocalDate.now());
            fkMapper.insert(fk);
        }
        return ResponseInfo.ok();
    }

    @Override
    public List<YfApExportVo> detExpAdtCustom(List<YfApAmt> dtoList) {
        Integer mtl = 0;
        Integer cost = 1;
        String companyId = UserIdThread.get().getCompanyId();
        /*获取品牌键值*/
        Set<String> lotList = dtoList.stream().map(YfApAmt::getLotNo).collect(Collectors.toSet());
        List<Map<String,String>> brandList = headMapper.getBrandByLot(lotList,companyId);
        List<YfApExportVo> voList = BeanUtil.copyList(dtoList, YfApExportVo.class);
        if (brandList == null || brandList.isEmpty()){
            return voList;
        }
        Map<String, String> lotBrandMap = brandList.stream()
                .collect(Collectors.toMap(a -> a.get("lotNo"), a -> a.get("brand")));
        /*转换类型并补充数据*/
        voList.forEach(vo->vo.setBrand(lotBrandMap.get(vo.getLotNo())));
        /*区分不同来源表数据*/
        List<YfApExportVo> mtlVoList = voList.stream()
                .filter(vo -> vo.getOrgType() != null && vo.getOrgType().equals(mtl))
                .collect(Collectors.toList());
        List<YfApExportVo> costVoList = voList.stream()
                .filter(vo -> vo.getOrgType() != null && vo.getOrgType().equals(cost))
                .collect(Collectors.toList());
        if (ArrayUtils.isNoEmpyt(mtlVoList)) {
            /*补充费用结算部分数据*/
            mtlVoList = otherMapper.suppDetailQuery(mtlVoList, companyId);
        }
        if (ArrayUtils.isNoEmpyt(costVoList)) {
            /*补充费用结算部分数据*/
            costVoList = costMapper.suppDetailQuery(costVoList, companyId);
        }
        voList = voList.stream()
                .filter(vo -> vo.getOrgType() == null)
                .collect(Collectors.toList());
        voList.addAll(mtlVoList);
        voList.addAll(costVoList);
        return voList;
    }

    /**
     * 变动应付记录相关检测
     * @param slipNo 单据号
     * @param companyId 公司ID
     */
    private void fkUpCheck(String slipNo, String companyId) {
        /*获取数据*/
        LambdaQueryWrapper<YfApAmt> wrapper = new LambdaQueryWrapper<YfApAmt>()
                .select(YfApAmt::getCfmFlag)
                .eq(YfApAmt::getCompanyId, companyId)
                .eq(YfApAmt::getSlipNo, slipNo);
        List<YfApAmt> amts = amtMapper.selectList(wrapper);
        if (ArrayUtils.isEmpyt(amts)) {
            log.error("\n错误接口：apAmtFkDel\n错误原因：结算资料表【YF_AP_AMT】没有这条单据\n单据号：{}", slipNo);
            throw new ServiceErrorException("单据号码不存在");
        }
        Set<String> set = amts.stream()
                .filter(Objects::nonNull)
                .map(YfApAmt::getCfmFlag)
                .collect(Collectors.toSet());
        if (!set.contains(Y)) {
            log.error("\n错误接口：apAmtFkDel\n错误原因：尝试 删除||修改||新增 未检核数据的付款记录\n单据号：{}", slipNo);
            throw new ServiceErrorException("未检核数据不能付款");
        }
    }
}

